package com.toy.core.security.chain;


import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public abstract class FilterChainProxy implements Filter {
    //~ Static fields/initializers =====================================================================================

    private static final Log logger = LogFactory.getLog(FilterChainProxy.class);

    //~ Methods ========================================================================================================

    public void destroy() {
    	Filter[] filters = obtainAllDefinedFilters();
    	for (Filter filter : filters) {
			if (filter != null) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Destroying Filter defined in ApplicationContext: '" + filter.toString() + "'");
                }
                filter.destroy();
            }
		}
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        FilterInvocation fi = new FilterInvocation(request, response, chain);
        
        Filter[] filters = obtainAllDefinedFilters();
        if (filters.length == 0) {
            if (logger.isDebugEnabled()) {
                logger.debug(fi.getRequestUrl() + " has an empty filter list");
            }
            chain.doFilter(request, response);
            return;
        }
        
        //如果是web资源,不用验证
       if (  WebExcludeUrlSuffix.isWebResourceUrl(fi.getHttpRequest()) ){
    	   if (logger.isDebugEnabled()) {
               logger.debug(fi.getRequestUrl() + " is web resource ");
           }
    	   chain.doFilter(request, response);
           return;
       }
        
        VirtualFilterChain virtualFilterChain = new VirtualFilterChain(fi, filters);
        virtualFilterChain.doFilter(fi.getRequest(), fi.getResponse());
    }

    public void init(FilterConfig filterConfig) throws ServletException {
    	Filter[] filters = obtainAllDefinedFilters();
    	for (Filter filter : filters) {
    		 if (filter != null) {
                 if (logger.isDebugEnabled()) {
                     logger.debug("Initializing Filter defined in ApplicationContext: '" + filter.toString() + "'");
                 }
                 filter.init(filterConfig);
             }
		}
    }
    
    public  abstract Filter[] obtainAllDefinedFilters();
    
    //~ Inner Classes ==================================================================================================

    /**
     * A <code>FilterChain</code> that records whether or not {@link
     * FilterChain#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse)} is called.<p>This
     * <code>FilterChain</code> is used by <code>FilterChainProxy</code> to determine if the next <code>Filter</code>
     * should be called or not.</p>
     */
    private class VirtualFilterChain implements FilterChain {
        private FilterInvocation fi;
        private Filter[] additionalFilters;
        private int currentPosition = 0;

        public VirtualFilterChain(FilterInvocation filterInvocation, Filter[] additionalFilters) {
            this.fi = filterInvocation;
            this.additionalFilters = additionalFilters;
        }

        public void doFilter(ServletRequest request, ServletResponse response)
            throws IOException, ServletException {
            if (currentPosition == additionalFilters.length) {
                if (logger.isDebugEnabled()) {
                    logger.debug(fi.getRequestUrl()
                        + " reached end of additional filter chain; proceeding with original chain");
                }

                fi.getChain().doFilter(request, response);
            } else {
                currentPosition++;

                if (logger.isDebugEnabled()) {
                    logger.debug(fi.getRequestUrl() + " at position " + currentPosition + " of "
                        + additionalFilters.length + " in additional filter chain; firing Filter: '"
                        + additionalFilters[currentPosition - 1] + "'");
                }

                additionalFilters[currentPosition - 1].doFilter(request, response, this);
            }
        }
    }
}